package com.yanqu.road.server.manger.kowloon;

import com.yanqu.road.entity.config.skill.SkillInfo;
import com.yanqu.road.entity.config.system.NormalWeightItem;
import com.yanqu.road.entity.enums.eSkillType;
import com.yanqu.road.entity.kowloon.config.KowloonMissionRoadConfig;
import com.yanqu.road.entity.kowloon.config.KowloonNpcConfig;
import com.yanqu.road.logic.model.kowloon.KowloonConfig;
import com.yanqu.road.entity.kowloon.enums.eKowloonBlockEffectType;
import com.yanqu.road.entity.kowloon.enums.eKowloonBlockType;
import com.yanqu.road.entity.kowloon.enums.eKowloonOpType;
import com.yanqu.road.entity.kowloon.sanxiao.*;
import com.yanqu.road.logic.model.kowloon.sanxiao.*;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.utils.RandomHelper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.*;

public class KowloonEliminateLogic {

    private static Logger logger = LogManager.getLogger(KowloonEliminateLogic.class.getName());

    /**
     * 移动方块，消除方块
     */
    public static KowloonEliminateResult moveBlock(KowloonEliminateParam param) {
        Random random = new Random();

        // 参数
        List<KowloonSkill> skillList = param.getGameData().getSkillList();
        List<List<KowloonBlock>> listList = param.getGameData().getBlockList();

        int fromX = param.getFromX();
        int fromY = param.getFromY();
        int toX = param.getToX();
        int toY = param.getToY();
        boolean isFirstMove = param.isFirstMove();
        int ret = 0;

        List<KowloonShowResult> showResultList = new ArrayList<>(); // 每轮结果
        KowloonShowResult firstShowResult = new KowloonShowResult();
        showResultList.add(firstShowResult);
        Set<KowloonBlock> dropSet = new HashSet<>(); // 上一轮掉落的方块

        //先检查有没有福袋开启中
        if (param.getGameData().getSkillBlockIndex() != 0 && param.getType() != eKowloonOpType.ChoseSkill.getValue()) {
            return new KowloonEliminateResult(GameErrorCode.E_ERROR_DATA);
        }

        // 技能增加步数
        int skillAddStep = 0;
        if (param.getType() == eKowloonOpType.Exchange.getValue()) {
            // 校验移动参数
            ret = checkMoveParam(listList, fromX, fromY, toX, toY);
            if (ret != 0) {
                return new KowloonEliminateResult(ret);
            }
            // 交换格子位置
            exchangeBlock(listList, fromX, fromY, toX, toY, showResultList);

        } else if (param.getType() == eKowloonOpType.MoveOut.getValue() || param.getType() == eKowloonOpType.Destroy.getValue()) {
            if (!xyExist(listList, fromX, fromY)) {
                ret = GameErrorCode.E_ERROR_DATA;
                return new KowloonEliminateResult(ret);
            }
            moveOutBlock(listList, fromX, fromY, toX, toY, param.getType(), showResultList);
            Set<KowloonBlock> newDropSet = new HashSet<>(); //新掉落的方块
            dropSet = dropBlock(listList, newDropSet, random, isFirstMove, param);
            isFirstMove = false;

            KowloonRoundResult roundResult = new KowloonRoundResult();
            roundResult.setListList(cloneBlockList(listList));
            showResultList.get(showResultList.size()-1).getRoundResults().add(roundResult);

            handleReplaceNewDropBlockSkill(listList, new ArrayList<>(newDropSet), showResultList, skillList, param);

        } else if (param.getType() == eKowloonOpType.ChoseSkill.getValue()) {
            int skillId = param.getFromX();
            fromY = param.getGameData().getSkillBlockIndex() / 10000;
            fromX = param.getGameData().getSkillBlockIndex() % 10000;
            if (!param.getGameData().getChooseSkill().contains(skillId)) {
                ret = GameErrorCode.E_ERROR_DATA;
                return new KowloonEliminateResult(ret);
            }

            if (!xyExist(listList, fromX, fromY)) {
                ret = GameErrorCode.E_ERROR_DATA;
                return new KowloonEliminateResult(ret);
            }

            //选完技能,移出方块
            moveOutBlock(listList, fromX, fromY, toX, toY, param.getType(), showResultList);

            int beforeStep = param.getGameData().getLeftStep();
            //判断是改变盘面的技能,先做技能效果再下落
            handleChoseSkill(skillId, listList, showResultList, param);
            int afterStep = param.getGameData().getLeftStep();
            skillAddStep = afterStep - beforeStep;

            Set<KowloonBlock> newDropSet = new HashSet<>(); //新掉落的方块
            dropSet = dropBlock(listList, newDropSet, random, isFirstMove, param);
            isFirstMove = false;

            KowloonRoundResult roundResult = new KowloonRoundResult();
            roundResult.setListList(cloneBlockList(listList));
            showResultList.get(showResultList.size() - 1).getRoundResults().add(roundResult);

            handleReplaceNewDropBlockSkill(listList, new ArrayList<>(newDropSet), showResultList, skillList, param);

            //重置福袋选择数据
            param.getGameData().setSkillBlockIndex(0);
            param.getGameData().setChooseSkill(new ArrayList<>());
        }

        int stepAdd = 0;
        int eliminateAmount = 0; //累计消除次数
        // 【可以消除了】
        for (int round = 1; round < 100; round++) { // 假装是while

            Set<KowloonBlock> roundEliminateSet = new HashSet<>(); // 本轮消除的格子
            KowloonShowResult showResult = showResultList.get(showResultList.size() - 1);

            // 正常消除
            handleNormalEliminate(param, listList, roundEliminateSet);

            if (roundEliminateSet.isEmpty()) {//这里已经没有可消除的了,不要产生roundResult数据
                break;
            }

            // 3. 消除
            setEliminateEmpty(listList, roundEliminateSet);

            // 4. 生成新方块
            KowloonCreateBlockResult createBlockResult = KowloonBlockComposeLogic.getCreateBlockList(round, fromX, fromY, toX, toY,
                    roundEliminateSet, dropSet, param);
            List<KowloonBlock> createBlockList = createBlockResult.getBlockList();
            eliminateAmount++;
            int nowStepAdd = param.getConfig().getEliminateAmountStepAdd(eliminateAmount);
            if (nowStepAdd > 0) {
                for (KowloonBlock block : roundEliminateSet) {
                    block.setEliminateAmount(eliminateAmount);
                    block.setStepAdd(nowStepAdd);
                    break;
                }
            }
            stepAdd += nowStepAdd;

            // 新方块添加到盘面
            List<KowloonBlock> realCreateBlockList = new ArrayList<>();
            for (KowloonBlock block : createBlockList) {
                listList.get(block.getY()).set(block.getX(), block.clone());

                //只按形状消除,按形状消除完剩下的，是不算消除的
                if (block.getLevel() > 0) {
                    realCreateBlockList.add(block);
                }else {
                    roundEliminateSet.remove(block);
                }
            }

            // 累计消除方块数
//            int eliminateBlockAmount = param.getGameData().getEliminateBlockAmount() + roundEliminateSet.size();
//            param.getGameData().setEliminateBlockAmount(eliminateBlockAmount);
//            if (Config.isDebug()) {
//                logger.info("-----消除累计{}-------", eliminateBlockAmount);
//            }

            // 5. 掉落方块
            Set<KowloonBlock> newDropSet = new HashSet<>(); //新掉落的方块
            dropSet = dropBlock(listList, newDropSet, random, isFirstMove, param);

            List<List<KowloonBlock>> roundEndBlockList = cloneBlockList(listList); // 备份下盘面

            // 7. 设置结果
            KowloonRoundResult roundResult = new KowloonRoundResult();
            roundResult.setElimateBlockList(new ArrayList<>(roundEliminateSet));
            roundResult.setCreateBlock(realCreateBlockList);
            roundResult.setListList(roundEndBlockList);

            showResult.getRoundResults().add(roundResult);

            //处理修改下落方块技能,改变方块类型
            handleReplaceNewDropBlockSkill(listList, new ArrayList<>(newDropSet), showResultList, skillList, param);

            // 8. 判断重排,卖鱼佬可以有不能消除的不用重排
//            checkRearrange(listList, roundResult);

            isFirstMove = false;
            if (roundEliminateSet.isEmpty()) {
                // 本轮没有消除，结束了
                break;
            }
        }

        //盘面稳定,开技能福袋
        handleSkillOpen(param, listList, showResultList);

        //消除过程增加了步数
        param.getGameData().addLeftStep(stepAdd);

        // 设置结果
        KowloonEliminateResult result = new KowloonEliminateResult();
        result.setShowResultList(showResultList);
        result.setEliminateAmount(eliminateAmount);
        result.setEliminateAddStep(stepAdd);
        result.setSkillAddStep(skillAddStep);
        return result;
    }

    /**
     * 复制一份盘面
     */
    public static List<List<KowloonBlock>> cloneBlockList(List<List<KowloonBlock>> param) {
        List<List<KowloonBlock>> tmpBlock = new ArrayList<>();
        for (List<KowloonBlock> list : param) {
            List<KowloonBlock> tmp = new ArrayList<>();
            for (KowloonBlock block : list) {
                tmp.add(block.clone());
            }
            tmpBlock.add(tmp);
        }
        return tmpBlock;
    }

    /**
     * 掉落方块
     */
    private static Set<KowloonBlock> dropBlock(List<List<KowloonBlock>> listList, Set<KowloonBlock> newDropSet, Random random, boolean isFirstMove,
                                               KowloonEliminateParam param) {
        KowloonConfig config = param.getConfig();
        Set<KowloonBlock> dropSet = new HashSet<>();
        int emptyNum = 0;
        Map<Integer, Integer> colorCountMap = new HashMap<>();
        // 旧方块掉落
        for (int y = listList.size() - 1; y >= 0; y--) {
//        for (int y = 0; y < listList.size(); y++) {
            for (int x = 0; x < listList.get(y).size(); x++) {
                KowloonBlock block = listList.get(y).get(x);
                if (block == null) {
                    // 找下面的格子掉落
                    for (int upY = y - 1; upY >= 0; upY--) {
                        if (listList.get(upY).get(x) != null) {
                            KowloonBlock up = listList.get(upY).get(x).clone(); // 复制一个出来
                            up.setY(y);
                            listList.get(y).set(x, up);
                            listList.get(upY).set(x, null);
                            dropSet.add(up); // 记录掉落格子
                            break;
                        }
                    }
                    emptyNum++;
                } else {
                    int num = colorCountMap.getOrDefault(block.getColor(), 0);
                    colorCountMap.put(block.getColor(), num + 1);
                }
            }
        }

        // 准备权重
        //根据配置取本关要掉落的方块类型
        KowloonMissionRoadConfig kowloonMissionRoadConfig = config.getKowloonMissionRoadConfig(param.getGameData().getChapterId(), param.getGameData().getSectionId());
        List<Integer> weightList = new ArrayList<>();
        // 获取场面上的方块
        Map<Integer, Integer> colorMap = new HashMap<>();
        for (int color = 1; color <= KowloonConfig.ELEMENT_NUM - 1; color++) {
            weightList.add(0);
            colorMap.put(color, 0);
        }
        // 银锭权重
        int silverIngotWeights = kowloonMissionRoadConfig.getSilverIngotWeights();
        // 权重加成
        List<Integer> weightAddList = kowloonMissionRoadConfig.getWeightAddList();
        for (List<KowloonBlock> kowloonBlocks : param.getGameData().getBlockList()) {
            for (KowloonBlock kowloonBlock : kowloonBlocks) {
                if (kowloonBlock == null) {
                    continue;
                }
                int color = kowloonBlock.getColor();
                eKowloonBlockType blockType = com.yanqu.road.entity.kowloon.enums.eKowloonBlockType.forValue(KowloonConfig.getTypeByColor(param.getGameData().getChooseHeroList(), color));
                if (blockType == null || blockType == eKowloonBlockType.Silver) {
                    continue;
                }
                colorMap.put(color, colorMap.getOrDefault(color, 0) + 1);
            }
        }
        List<NormalWeightItem> weightItemList = new ArrayList<>();
        for (Map.Entry<Integer, Integer> entry : colorMap.entrySet()) {
            weightItemList.add(new NormalWeightItem(BigInteger.valueOf(entry.getKey()), entry.getValue()));
        }
        weightItemList.sort((o1, o2) -> o2.getWeight() - o1.getWeight());

        // 颜色数量从多到少
        for (int i = 0; i < weightAddList.size(); i++) {
            if (i == 4) {
                // i=4 就是银锭权重
                silverIngotWeights += weightAddList.get(i);
            } else {
                int color = weightItemList.get(i).getValue().intValue();
                weightList.set(color - 1, weightList.get(color - 1) + weightAddList.get(i));
            }
        }
        // 最后加上银锭权重
        weightList.add(silverIngotWeights);

        //新手引导掉落
        List<Integer> guideDrop = new ArrayList<>();
        if (param.isGuide()) {
            guideDrop = param.getConfig().getGuideDrop(param.getGameData().getChapterId(), param.getGameData().incGuideDropIndex());
        }

        // 掉落新方块
        for (int y = listList.size() - 1; y >= 0; y--) {
            for (int x = 0; x < listList.get(y).size(); x++) {
                if (listList.get(y).get(x) == null) {
                    KowloonBlock block;
                    int color = 0;
                    if (!guideDrop.isEmpty()) {
                        color = guideDrop.get(0);
                        guideDrop.remove(0);
                    } else {
                        color = RandomHelper.getRandomIndexByWeight(weightList, random) + 1;
                    }

                    int blockType = KowloonConfig.getTypeByColor(param.getGameData().getChooseHeroList(),color);
                    block = new KowloonBlock(x, y, blockType);
                    block.setColor(color);

                    listList.get(y).set(x, block);
                    dropSet.add(block); // 记录掉落格子

                    newDropSet.add(block);
                }
            }
        }

        return dropSet;
    }

    /**
     * 把消除的方块设置为null
     */
    private static void setEliminateEmpty(List<List<KowloonBlock>> listList, Set<KowloonBlock> eliminateSet) {
        for (KowloonBlock block : eliminateSet) {
            listList.get(block.getY()).set(block.getX(), null);
        }
    }


    /**
     * 处理正常消除
     */
    private static void handleNormalEliminate(KowloonEliminateParam param, List<List<KowloonBlock>> listList,
                                              Set<KowloonBlock> eliminateSet) {

        // 看看哪些方块可以消除
        List<KowloonBlock> eliminateList = new ArrayList<>();
        for (List<KowloonBlock> list : listList) {
            for (KowloonBlock block : list) {
                if (eliminateSet.contains(block)) {
                    continue; // 已消除的跳过，避免重复爆炸
                }

                if (!blockCanEliminate(param, listList, block)) {
                    continue;
                }

                // 添加
                eliminateList.add(block);
            }
        }

        // 添加消除
        eliminateSet.addAll(eliminateList);
    }

    /**
     * 判断单个点能否消除
     */
    private static boolean blockCanEliminate(KowloonEliminateParam param, List<List<KowloonBlock>> listList, KowloonBlock block) {

        int x = block.getX();
        int y = block.getY();
        int eliminateType = block.getEliminateType();
        if (block.getType() == eKowloonBlockType.Skill.getValue()) {
            return false;
        }
        // 好汉判断下合成等级上限
        if (eKowloonBlockType.isHero(block.getType())) {
            KowloonChooseHeroData chooseHero = param.getGameData().getChooseHero(block.getType());
            if (chooseHero != null && block.getLevel() >= param.getConfig().getMaxMergeLevel(chooseHero.getHeroId(), chooseHero.getLevel())) {
                return false;
            }
        }
        // 连续同色的数量
        int num = 0;

        // 1. 左到右
        for (int tmpX = x - 2; tmpX <= x + 2; tmpX++) {
            if (xyExist(listList, tmpX, y) && eliminateType == listList.get(y).get(tmpX).getEliminateType()) {
                num++;
            } else {
                num = 0;
            }
            if (num >= 3) {
                return true;
            }
        }

        // 2. 下到上
        for (int tmpY = y - 2; tmpY <= y + 2; tmpY++) {
            if (xyExist(listList, x, tmpY) && eliminateType == listList.get(tmpY).get(x).getEliminateType()) {
                num++;
            } else {
                num = 0;
            }
            if (num >= 3) {
                return true;
            }
        }

        return false;
    }

    /**
     * 检查移动参数
     */
    private static int checkMoveParam(List<List<KowloonBlock>> listList, int fromX, int fromY, int toX, int toY) {
        if (fromX == 0 && fromY == 0 && toX == 0 && toY == 0) {
            return 0; // 检查斜消
        }
        int xDis = Math.abs(fromX - toX);
        int yDis = Math.abs(fromY - toY);
        if (!xyExist(listList, fromX, fromY) || // 坐标要存在
                !xyExist(listList, fromX, fromY) || // 坐标要存在
                xDis > 1 || yDis > 1 // 同一方向距离不超过1
        ) { // 无法斜向移动时，只能有一个方向距离是1
            return GameErrorCode.E_OPERA_MASK_BLOCK_NO_ADJOIN;
        }
        return 0;
    }

    /**
     * 交换两个格子
     */
    private static void exchangeBlock(List<List<KowloonBlock>> blockListList, int x1, int y1, int x2, int y2, List<KowloonShowResult> showResultList) {
        KowloonBlock block1 = blockListList.get(y1).get(x1);
        KowloonBlock block2 = blockListList.get(y2).get(x2);

        block1.setX(x2);
        block1.setY(y2);
        blockListList.get(y2).set(x2, block1);


        block2.setX(x1);
        block2.setY(y1);
        blockListList.get(y1).set(x1, block2);

        KowloonBlockEffect effect = new KowloonBlockEffect(eKowloonBlockEffectType.Exchange.getValue());
        KowloonBlock clone1 = block1.clone();
        KowloonBlock clone2 = block2.clone();
        effect.getBlockList().add(clone1);
        effect.getBlockList().add(clone2);
        showResultList.get(showResultList.size() - 1).getSkillEffectList().add(effect);
    }

    /**
     * 交换两个格子
     */
    private static KowloonBlockEffect exchangeBlock(List<List<KowloonBlock>> blockListList, int x1, int y1, int x2, int y2) {
        KowloonBlock block1 = blockListList.get(y1).get(x1);
        KowloonBlock block2 = blockListList.get(y2).get(x2);

        block1.setX(x2);
        block1.setY(y2);
        blockListList.get(y2).set(x2, block1);


        block2.setX(x1);
        block2.setY(y1);
        blockListList.get(y1).set(x1, block2);

        KowloonBlockEffect effect = new KowloonBlockEffect(eKowloonBlockEffectType.Exchange.getValue());
        effect.getBlockList().add(block1);
        effect.getBlockList().add(block2);

        return effect;
    }

    /**
     * 销毁格子
     */
    private static void moveOutBlock(List<List<KowloonBlock>> blockListList, int x1, int y1, int toX, int toY, int opType, List<KowloonShowResult> showResultList) {
        KowloonBlock block1 = blockListList.get(y1).get(x1);

        blockListList.get(y1).set(x1, null);

        int effectType = eKowloonBlockEffectType.MoveOut.getValue();
        if (opType == eKowloonOpType.Destroy.getValue()) {
            effectType = eKowloonBlockEffectType.Destroy.getValue();
        } else if (opType == eKowloonOpType.MoveOut.getValue()) {
            effectType = eKowloonBlockEffectType.MoveOut.getValue();
        } else if (opType == eKowloonOpType.ChoseSkill.getValue()) {
            effectType = eKowloonBlockEffectType.Destroy.getValue();
        }
        KowloonBlockEffect effect = new KowloonBlockEffect(effectType);
        effect.getBlockList().add(block1);
        if (toX != 0 || toY != 0) {
            effect.getBlockList().add(new KowloonBlock(toX, toY, 0));
        }

        showResultList.get(showResultList.size() - 1).getSkillEffectList().add(effect);
    }

    /**
     * 坐标是否存在
     */
    private static boolean xyExist(List<List<KowloonBlock>> blockListList, int x, int y) {
        if (x < 0 || y < 0) {
            return false;
        }
        if (blockListList.size() > y) {
            if (blockListList.get(y).size() > x) {
                return blockListList.get(y).get(x) != null;
            }
        }
        return false;
    }

    /**
     * 当前盘面能否消除
     */
    private static boolean canEliminate(List<List<KowloonBlock>> listList) {

        // 正常消除需要3个
        for (int y = 0; y < listList.size(); y++) {
            for (int x = 0; x < listList.get(y).size(); x++) {
                KowloonBlock kowloonBlock = listList.get(y).get(x);
                int blockType = kowloonBlock.getType();
                int eliminateType = kowloonBlock.getEliminateType();
                if (blockType == 0) {
                    continue;
                }
                if (blockType == eKowloonBlockType.Skill.getValue()) { //技能福袋
                    continue;
                }
                // 右
                if (xyExist(listList, x + 1, y) && xyExist(listList, x + 2, y) &&
                        eliminateType == listList.get(y).get(x + 1).getEliminateType() &&
                        eliminateType == listList.get(y).get(x + 2).getEliminateType()) {
                    return true;
                }

                // 上
                if (xyExist(listList, x, y + 1) && xyExist(listList, x, y + 2) &&
                        eliminateType == listList.get(y + 1).get(x).getEliminateType() &&
                        eliminateType == listList.get(y + 2).get(x).getEliminateType()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 检查重排
     */
    private static void checkRearrange(KowloonEliminateParam param, List<List<KowloonBlock>> listList,
                                       KowloonRoundResult roundResult) {

        // 没返回就是要重排了
        KowloonEliminateTip eliminateTips = getEliminateTip(param, listList);
        if (eliminateTips != null) {
            return;
        }

        // 打乱格子
        List<KowloonBlock> allList = new ArrayList<>();
        for (List<KowloonBlock> list : listList) {
            allList.addAll(list);
        }
        Collections.shuffle(allList);

        // 重排
        int index = 0;
        for (int y = 0; y < listList.size(); y++) {
            for (int x = 0; x < listList.get(y).size(); x++) {
                KowloonBlock block = allList.get(index++).clone();
                block.setX(x);
                block.setY(y);
                listList.get(y).set(x, block);
            }
        }

        // 添加重排结果
        roundResult.setListList(listList);
    }

    /**
     * 获取消除提示
     */
    public static KowloonEliminateTip getEliminateTip(KowloonEliminateParam param, List<List<KowloonBlock>> listList) {
        // 复制一份出来
        listList = cloneBlockList(listList);

        // 遍历每个方块尝试交换
        for (int y = 0; y < listList.size(); y++) {
            for (int x = 0; x < listList.get(y).size(); x++) {

                int x2 = x + 1;
                int y2 = y;
                if (xyExist(listList, x2, y2)) {
                    exchangeBlock(listList, x, y, x2, y2);
                    if (canEliminate(listList)) {
                        Set<KowloonBlock> set = new HashSet<>();
                        handleNormalEliminate(param, listList, set);
                        exchangeBlock(listList, x, y, x2, y2);
                        return new KowloonEliminateTip(listList.get(y).get(x), listList.get(y2).get(x2), set);
                    }
                    exchangeBlock(listList, x, y, x2, y2);
                }

                x2 = x + 1;
                y2 = y + 1;

                x2 = x;
                y2 = y + 1;
                if (xyExist(listList, x2, y2)) {
                    exchangeBlock(listList, x, y, x2, y2);
                    if (canEliminate(listList)) {
                        Set<KowloonBlock> set = new HashSet<>();
                        handleNormalEliminate(param, listList, set);
                        exchangeBlock(listList, x, y, x2, y2);
                        return new KowloonEliminateTip(listList.get(y).get(x), listList.get(y2).get(x2), set);
                    }
                    exchangeBlock(listList, x, y, x2, y2);
                }

                x2 = x - 1;
                y2 = y + 1;
            }
        }

        return null;
    }

    /**
     * 获取交换后的消除提示
     */
    public static KowloonEliminateTip getOperateEliminateTip(KowloonEliminateParam param, List<List<KowloonBlock>> listList) {
        // 复制一份出来
        listList = cloneBlockList(listList);
        exchangeBlock(listList, param.getFromX(), param.getFromY(), param.getToX(), param.getToY());
        if (canEliminate(listList)) {
            Set<KowloonBlock> set = new HashSet<>();
            handleNormalEliminate(param, listList, set);
            return new KowloonEliminateTip(listList.get(param.getFromY()).get(param.getFromX()), listList.get(param.getToY()).get(param.getToX()), set);
        }

        return null;
    }

    /**
     * 获取随机盘面
     */
    public static List<List<KowloonBlock>> getRandomBlockList(KowloonEliminateParam param) {
        int width = KowloonConfig.ELIMINATE_WIDTH;
        int height = KowloonConfig.ELIMINATE_HEIGHT;
        int colorCount = 5;

        Random random = new Random();

        // 低概率事件，10次很稳了
        for (int i = 0; i < 10; i++) {

            // 先把格子填满
            List<List<KowloonBlock>> listList = new ArrayList<>();
            for (int y = 0; y < height; y++) {
                List<KowloonBlock> list = new ArrayList<>();
                for (int x = 0; x < width; x++) {
                    list.add(new KowloonBlock(x, y, 0));
                }
                listList.add(list);
            }

            // 随机颜色
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    List<Integer> list = getCanCreateColor(listList, colorCount, x, y);
                    if (list.isEmpty()) {
                        // 走到这里说明有问题，生成个炸弹让他稳住
                        listList.get(y).get(x).setType(eKowloonBlockType.Skill.getValue());
                    } else {
                        int index = random.nextInt(list.size());
                        int color = list.get(index);
                        listList.get(y).get(x).setColor(color);
                        listList.get(y).get(x).setType(KowloonConfig.getTypeByColor(param.getGameData().getChooseHeroList(), color));
                    }
                }
            }

            // 看看能不能消除
            KowloonEliminateTip tip = KowloonEliminateLogic.getEliminateTip(param, listList);
            if (tip != null) {
                return listList;
            }
        }

        // 说明代码有问题
        throw new RuntimeException("kowloon getRandomBlockList error!");
    }

    /**
     * 根据配置生成盘面
     */
    public static List<List<KowloonBlock>> getBlockListByConfig(KowloonEliminateParam param, KowloonConfig config, int chapterId) {

        KowloonMissionRoadConfig kowloonMissionRoadConfig = config.getKowloonMissionRoadConfig(chapterId, 0);
        if (null == kowloonMissionRoadConfig) {
            return null;
        }
        int width = KowloonConfig.ELIMINATE_WIDTH;
        int height = KowloonConfig.ELIMINATE_HEIGHT;
//        int colorCount = KowloonConfig.ELEMENT_NUM;

        int totalBlockNum = width * height;

        List<Integer> weightList = new ArrayList<>();
        int totalWeight = 0;

        // 方案一：权重不打乱
//        for (int color = 1; color <= colorCount; color++) {
//            int elementWeight = kowloonMissionRoadConfig.getElementWeight(color);
//            totalWeight += elementWeight;
//            weightList.add(elementWeight);
//        }

        // 方案二：权重打乱
        for (Integer elementWeight : config.getDropElementWeightList(chapterId, 0)) {
            totalWeight += elementWeight;
            weightList.add(elementWeight);
        }


        List<Integer> colors = new ArrayList<>();

        for (int color = 1; color <= weightList.size(); color++) {
            int elementWeight = weightList.get(color - 1);
            int colorNum = BigDecimal.valueOf(elementWeight).divide(BigDecimal.valueOf(totalWeight), 8, RoundingMode.DOWN).multiply(BigDecimal.valueOf(totalBlockNum)).setScale(0, RoundingMode.DOWN).intValue();
            for (int i = 0; i < colorNum; i++) {
                colors.add(color);
            }
        }

        //不足的补银锭的颜色5
        if (colors.size() < totalBlockNum) {
            for (int i = colors.size(); i <= totalBlockNum; i++) {
                colors.add(5);
            }
        }

        //随机一下
        Collections.shuffle(colors);

        List<List<KowloonBlock>> listList = new ArrayList<>();
        for (int y = 0; y < height; y++) {
            List<KowloonBlock> list = new ArrayList<>();
            for (int x = 0; x < width; x++) {
                int index = y * width + x;
                int color = colors.get(index);
                KowloonBlock block = new KowloonBlock(x, y, 0);
                block.setColor(color);
                block.setType(KowloonConfig.getTypeByColor(param.getGameData().getChooseHeroList(), color));
                list.add(block);
            }
            listList.add(list);
        }
        return listList;
    }

    private static List<Integer> getCanCreateColor(List<List<KowloonBlock>> listList, int colorCount, int x, int y) {
        Set<Integer> removeSet = new HashSet<>();
        // 下面2个颜色相同
        if (y - 2 >= 0
                && listList.get(y - 1).get(x).getColor() == listList.get(y - 2).get(x).getColor()) {
            removeSet.add(listList.get(y - 1).get(x).getColor());
        }
        // 左边2个颜色相同
        if (x - 2 >= 0
                && listList.get(y).get(x - 1).getColor() == listList.get(y).get(x - 2).getColor()) {
            removeSet.add(listList.get(y).get(x - 1).getColor());
        }
        // 结果
        List<Integer> list = new ArrayList<>();
        for (int i = 1; i <= colorCount; i++) {
            if (!removeSet.contains(i)) {
                list.add(i);
            }
        }
        return list;
    }


    public static List<List<KowloonBlock>> getTestListList(List<KowloonChooseHeroData> chooseHeroDataList) {
        int[][] gridArry = {
                {1, 1, 3, 1, 1, 2},
                {2, 2, 1, 2, 3, 1},
                {1, 4, 2, 1, 3, 4},
                {5, 3, 1, 5, 2, 4},
                {1, 2, 1, 3, 4, 1},
                {1, 1, 1, 1, 1, 1},
        };
        List<List<KowloonBlock>> listList = new ArrayList<>();
        for (int y = 0; y < gridArry.length; y++) {
            listList.add(new ArrayList<>());
            for (int x = 0; x < gridArry[y].length; x++) {
                int color = gridArry[y][x];
                int blockType = KowloonConfig.getTypeByColor(chooseHeroDataList, color);
                KowloonBlock block = new KowloonBlock(x, y, blockType);
                block.setColor(color);
                listList.get(y).add(block);
            }
        }

        return listList;
    }


    /**
     * 处理开技能福袋
     *
     * @param param
     * @param listList
     * @param showResultList
     * @return
     */
    private static void handleSkillOpen(KowloonEliminateParam param, List<List<KowloonBlock>> listList, List<KowloonShowResult> showResultList) {
        KowloonConfig config = param.getConfig();
        KowloonGameData gameData = param.getGameData();
        List<Integer> notIncludeSkillId = new ArrayList<>();
        for (KowloonSkill skill : gameData.getSkillList()) {
            SkillInfo skillInfo = param.getKowloonSkillInfo(skill.getSkillId());
            if (null == skillInfo) {
                continue;
            }
            eSkillType eType = eSkillType.valueOf(skillInfo.getType());
            if (null == eType) {
                continue;
            }
            KowloonSkill kowloonSkill = gameData.getSkill(skill.getSkillId());
            if (null != kowloonSkill) {
                //判断堆叠上限
                if (kowloonSkill.getNum() >= skillInfo.getMaxLevel()) {//超过堆叠上限不随机
                    notIncludeSkillId.add(skill.getSkillId());
                }
            }
        }
        // 场上的基础方块数
        Map<Integer, Integer> baseElementNumMap = new HashMap<>();
        for (List<KowloonBlock> blockList : gameData.getBlockList()) {
            for (KowloonBlock block : blockList) {
                if (block.getLevel() == 0 && block.getType() != eKowloonBlockType.Silver.getValue()) {
                    int elementId = block.getType();
                    if (elementId == -1) {
                        continue;
                    }
                    int num = baseElementNumMap.getOrDefault(elementId, 0);
                    num++;
                    baseElementNumMap.put(elementId, num);
                }
            }
        }

        // 当前上场的元素
        List<Integer> chooseElementList = new ArrayList<>();
        for (KowloonChooseHeroData heroData : gameData.getChooseHeroList()) {
            int elementId = config.getElementIdByHero(heroData.getHeroId());
            if (elementId == -1) {
                continue;
            }
            // 场上没有这个基础元素,不生成对应好汉的转换技能。
            if (baseElementNumMap.getOrDefault(elementId, 0) == 0) {
                notIncludeSkillId.add(config.getSkillIdByTypeAndElementId(eSkillType.KowloonSkill175.getValue(), elementId));
                notIncludeSkillId.add(config.getSkillIdByTypeAndElementId(eSkillType.KowloonSkill176.getValue(), elementId));
            } else {
                chooseElementList.add(elementId);
            }
        }


        // 好汉技能
        Map<Integer, List<NormalWeightItem>> map = new HashMap<>();
        for (KowloonChooseHeroData heroData : gameData.getChooseHeroList()) {
            int heroId = heroData.getHeroId();
            KowloonNpcConfig heroConfig = config.getKowloonHeroConfig(heroId, heroData.getLevel());
            // 初级技能
            List<NormalWeightItem> juniorList = map.getOrDefault(1, new ArrayList<>());
            juniorList.addAll(heroConfig.getJuniorSkillsList());
            map.put(1, juniorList);

            // 中级技能
            List<NormalWeightItem> middleList = map.getOrDefault(2, new ArrayList<>());
            middleList.addAll(heroConfig.getMiddleSkillsList());
            map.put(2, middleList);

            // 高级技能
            List<NormalWeightItem> highList = map.getOrDefault(3, new ArrayList<>());
            highList.addAll(heroConfig.getHighSkillsList());
            map.put(3, highList);
        }
        KowloonBlockEffect effect = null;
        for (List<KowloonBlock> yList : listList) {
            for (KowloonBlock xBlock : yList) {
                if (xBlock.getType() == eKowloonBlockType.Skill.getValue()) {
                    xBlock.setStatus(1);
                    gameData.setSkillBlockIndex(xBlock.getY() * 10000 + xBlock.getX());
                    //有的技能最多只能选一次要判断
                    List<NormalWeightItem> heroList = map.getOrDefault(xBlock.getLevel(), new ArrayList<>());
                    List<Integer> randomSkill = config.randomSkillList(gameData.getChapterId(), gameData.getSectionId(), xBlock.getLevel(), notIncludeSkillId, 3, heroList, chooseElementList);
                    gameData.setChooseSkill(randomSkill);

                    effect = new KowloonBlockEffect(eKowloonBlockEffectType.OpenBag.getValue());
                    effect.getBlockList().add(xBlock.clone());
                    break;
                }
            }
            if (null != effect) {
                break;
            }
        }

        if (null != effect) {
            KowloonShowResult showResult = new KowloonShowResult();
            showResult.getSkillEffectList().add(effect);
            KowloonRoundResult roundResult = new KowloonRoundResult();
            roundResult.setListList(cloneBlockList(listList));
            showResult.getRoundResults().add(roundResult);
            showResultList.add(showResult);
        }
    }

    /**
     * 处理变更掉落方块技能
     */
    private static void handleReplaceNewDropBlockSkill(List<List<KowloonBlock>> listList, List<KowloonBlock> newDropBlock, List<KowloonShowResult> showResultList, List<KowloonSkill> skillList, KowloonEliminateParam kowloonParam) {
        List<KowloonBlockEffect> effectList = new ArrayList<>();

        for (KowloonSkill kowloonSkill : skillList) {
            int skillId = kowloonSkill.getSkillId();
            SkillInfo skillInfo = kowloonParam.getKowloonSkillInfo(skillId);
            if (null == skillInfo || skillInfo.getType() != eSkillType.KowloonSkill176.getValue()) {
                continue;
            }

            int changeType = skillInfo.getParamList().get(3);

            KowloonBlockEffect effect = new KowloonBlockEffect(eKowloonBlockEffectType.Replace.getValue());
            effect.getValueList().add(changeType);
            for (KowloonBlock block : newDropBlock) {
                if (block.getType() == changeType) {
                    int newType = KowloonBlockComposeLogic.blockLvUpType(block.getType());
                    if (newType != block.getType()) {
                        block.setLevel(1);
                    } else {
                        block.setLevel(block.getLevel() + 1);
                    }
                    block.setType(newType);
                    effect.getBlockList().add(block.clone());
                }
            }
            if (!effect.getBlockList().isEmpty()) {
                effectList.add(effect);
            }
        }


        if (!effectList.isEmpty()) {
            KowloonShowResult newShowResult = new KowloonShowResult();
            newShowResult.getSkillEffectList().addAll(effectList);
            KowloonRoundResult newRoundResult = new KowloonRoundResult();
            //再复制一份盘面
            List<List<KowloonBlock>> roundEndBlockList = cloneBlockList(listList);
            newRoundResult.setListList(roundEndBlockList);
            newShowResult.getRoundResults().add(newRoundResult);
            showResultList.add(newShowResult);
        }
    }

    /**
     * 选择技能后处理
     */
    private static void handleChoseSkill(int skillId, List<List<KowloonBlock>> listList, List<KowloonShowResult> showResultList, KowloonEliminateParam param) {
        KowloonGameData gameData = param.getGameData();
        SkillInfo skillInfo = param.getKowloonSkillInfo(skillId);
        if (null == skillInfo) {
            return;
        }
        eSkillType eType = eSkillType.valueOf(skillInfo.getType());
        if (null == eType) {
            return;
        }
        KowloonSkill kowloonSkill = gameData.getSkill(skillId);
        if (null != kowloonSkill) {
            //判断堆叠上限
            if (kowloonSkill.getNum() >= skillInfo.getMaxLevel()) {//超过堆叠上限不生效
                return;
            }
            kowloonSkill.setNum(kowloonSkill.getNum() + 1);
        }
        switch (eType) {
            case KowloonSkill174: {
                int stepAdd = skillInfo.getParamList().get(3);
                KowloonBlockEffect effect = new KowloonBlockEffect(eKowloonBlockEffectType.AddStep.getValue());
                effect.getValueList().add(stepAdd);
                showResultList.get(showResultList.size() - 1).getSkillEffectList().add(effect);
                gameData.setLeftStep(gameData.getLeftStep() + stepAdd);
            }
            break;
            case KowloonSkill175:
            case KowloonSkill176: {
                int lvUpBlockType = skillInfo.getParamList().get(3);
                KowloonBlockEffect effect = new KowloonBlockEffect(eKowloonBlockEffectType.LevelUp.getValue());
                showResultList.get(showResultList.size() - 1).getSkillEffectList().add(effect);

                for (List<KowloonBlock> blockList : listList) {
                    for (KowloonBlock block : blockList) {
                        if (null != block && block.getType() == lvUpBlockType) {
                            int newType = KowloonBlockComposeLogic.blockLvUpType(lvUpBlockType);
                            block.setLevel(block.getLevel() + 1);
                            block.setType(newType);
                            effect.getBlockList().add(block.clone());
                        }
                    }
                }
            }
            break;
            case KowloonSkill177: {
                int rowNum = skillInfo.getParamList().get(3);
                KowloonBlockEffect effect = new KowloonBlockEffect(eKowloonBlockEffectType.RemoveRow.getValue());
                effect.getValueList().add(rowNum);
                for (int y = listList.size() - 1; y >= 0; y--) {
                    if (--rowNum < 0) {
                        break;
                    }
                    List<KowloonBlock> blockList = new ArrayList<>(listList.get(y));
                    for (int x = 0; x < blockList.size(); x++) {
                        if (null == blockList.get(x)) {
                            continue;
                        }
                        if (blockList.get(x).getLevel() != 0) { //移出基础元素和银锭,相当于移出没有等级的方块
                            continue;
                        }

                        effect.getBlockList().add(blockList.get(x).clone());
                        listList.get(y).set(x, null);//移出相当于置空
                    }
                }

                showResultList.get(showResultList.size() - 1).getSkillEffectList().add(effect);
            }
            break;
            case KowloonSkill178: {
                KowloonBlockEffect effect = new KowloonBlockEffect(eKowloonBlockEffectType.Rearrange.getValue());
                showResultList.get(showResultList.size() - 1).getSkillEffectList().add(effect);
                // 打乱格子
                List<KowloonBlock> allList = new ArrayList<>();
                for (List<KowloonBlock> list : listList) {
                    for (KowloonBlock block : list) {
                        if (null == block) {
                            continue;
                        }
                        if (eKowloonBlockType.isHero(block.getType())) {
                            continue;
                        }
                        allList.add(block);
                    }
                }
                Collections.shuffle(allList);

                // 重排
                int index = 0;
                for (int y = 0; y < listList.size(); y++) {
                    for (int x = 0; x < listList.get(y).size(); x++) {
                        KowloonBlock kowloonBlock = listList.get(y).get(x);
                        if (kowloonBlock == null) {
                            continue;
                        }
                        if (!allList.contains(kowloonBlock)) {
                            continue;
                        }
                        KowloonBlock randomBlock = allList.get(index++);
                        KowloonBlock block = randomBlock.clone();
                        block.setX(x);
                        block.setY(y);
                        effect.getBlockList().add(block.clone());
                        listList.get(y).set(x, block);
                    }
                }

            }
            break;
            case KowloonSkill179: {
                KowloonBlockEffect effect = new KowloonBlockEffect(eKowloonBlockEffectType.AddRow.getValue());
                showResultList.get(showResultList.size() - 1).getSkillEffectList().add(effect);

                List<KowloonBlock> blockList = new ArrayList<>();

                for (int x = 0; x < listList.get(0).size(); x++) {
                    blockList.add(null);
                }

                listList.add(blockList); //加一行空的

                effect.getValueList().add(listList.size());//发最终行数
            }
            break;
            case KowloonSkill181: {
                int randomNum = skillInfo.getParamList().get(2);
                int levelAdd = skillInfo.getParamList().get(3);
                KowloonBlockEffect effect = new KowloonBlockEffect(eKowloonBlockEffectType.LevelUp.getValue());
                showResultList.get(showResultList.size() - 1).getSkillEffectList().add(effect);

                List<KowloonBlock> baseElementList = new ArrayList<>();
                for (List<KowloonBlock> blockList : listList) {
                    for (KowloonBlock block : blockList) {
                        if (null == block) {
                            continue;
                        }
                        if (block.getType() == eKowloonBlockType.Silver.getValue()) {
                            continue;
                        }
                        if (block.getType() == eKowloonBlockType.Skill.getValue()) {
                            continue;
                        }
                        // 过滤已满级的好汉
                        if (eKowloonBlockType.isHero(block.getType())) {
                            KowloonChooseHeroData chooseHero = param.getGameData().getChooseHero(block.getType());
                            int maxMergeLevel = param.getConfig().getMaxMergeLevel(chooseHero.getHeroId(), chooseHero.getLevel());
                            if (block.getLevel() >= maxMergeLevel) {
                                continue;
                            }
                        }
                        baseElementList.add(block);
                    }
                }
                Collections.shuffle(baseElementList);

                for (int i = 0; i < baseElementList.size() && i < randomNum; i++) {
                    KowloonBlock block = baseElementList.get(i);
                    int newType = KowloonBlockComposeLogic.blockLvUpType(block.getType());
                    int maxMergeLevel = 0;
                    if (eKowloonBlockType.isHero(newType)) {
                        KowloonChooseHeroData chooseHero = param.getGameData().getChooseHero(newType);
                        maxMergeLevel = param.getConfig().getMaxMergeLevel(chooseHero.getHeroId(), chooseHero.getLevel());

                    }
                    // 好汉判断下合成等级上限
                    for (int j = 0; j < levelAdd; j++) {
                        if (block.getLevel() >= maxMergeLevel) { //超过最大合成等级就不能再升级了
                            break;
                        }
                        newType = KowloonBlockComposeLogic.blockLvUpType(block.getType());
                        block.setLevel(block.getLevel() + 1);
                        block.setType(newType);
                    }
                    effect.getBlockList().add(block.clone());
                }

            }
            break;
            case KowloonSkill182:
            case KowloonSkill183:
            case KowloonSkill184:
            case KowloonSkill185:
            case KowloonSkill186:
            case KowloonSkill187:
            case KowloonSkill188:
            case KowloonSkill189:
            case KowloonSkill190:
            case KowloonSkill199: {
                KowloonBlockEffect effect = new KowloonBlockEffect(eKowloonBlockEffectType.HeroBuff.getValue());
                showResultList.get(showResultList.size() - 1).getSkillEffectList().add(effect);

                int compareType = skillInfo.getParamList().get(2);
                for (List<KowloonBlock> blockList : listList) {
                    for (KowloonBlock block : blockList) {
                        if (null == block) {
                            continue;
                        }
                        if (block.getLevel() == 0) {
                            continue;
                        }
                        if (block.getType() == eKowloonBlockType.Skill.getValue()) {
                            continue;
                        }
                        if (compareType != 0 && compareType != block.getType()) {
                            continue;
                        }
                        effect.getBlockList().add(block.clone());
                    }
                }
            }
            break;
            case KowloonSkill191: {
                int hpRevert = skillInfo.getParamList().get(3);
                if (hpRevert > 0) {
                    gameData.hpRevert(hpRevert);
                }
                KowloonBlockEffect effect = new KowloonBlockEffect(eKowloonBlockEffectType.Hp.getValue());
                effect.getValueList().add(gameData.getHp());
                effect.getValueList().add(gameData.getMaxHp());
                showResultList.get(showResultList.size() - 1).getSkillEffectList().add(effect);

                handleSkill193(listList, showResultList, param, hpRevert);
            }
            break;
            case KowloonSkill192: {
                int hpLimitAdd = skillInfo.getParamList().get(3);
                if (hpLimitAdd > 0) {
                    gameData.addHpMax(hpLimitAdd);
                }

                KowloonBlockEffect effect = new KowloonBlockEffect(eKowloonBlockEffectType.Hp.getValue());
                effect.getValueList().add(gameData.getHp());
                effect.getValueList().add(gameData.getMaxHp());
                showResultList.get(showResultList.size() - 1).getSkillEffectList().add(effect);
            }
            break;
        }

        if (null == kowloonSkill) {
            kowloonSkill = new KowloonSkill();
            kowloonSkill.setSkillId(skillId);
            kowloonSkill.setNum(1);
            kowloonSkill.setValue(0);
            gameData.addSkill(kowloonSkill);
        }
    }

    /**
     * 处理技能193,每恢复多少血提升N3好汉的初始攻击千分比
     */
    private static void handleSkill193(List<List<KowloonBlock>> listList, List<KowloonShowResult> showResultList, KowloonEliminateParam param, int valueAdd) {
        KowloonGameData gameData = param.getGameData();
        SkillInfo skill193Info = null;
        KowloonSkill kowloonSkill193 = null;
        for (KowloonSkill kowloonSkill : gameData.getSkillList()) {
            SkillInfo skillInfo = param.getKowloonSkillInfo(kowloonSkill.getSkillId());
            if (null == skillInfo) {
                continue;
            }
            if (skillInfo.getType() == eSkillType.KowloonSkill193.getValue()) {
                skill193Info = skillInfo;
                kowloonSkill193 = kowloonSkill;
                break;
            }
        }
        if (null == skill193Info || null == kowloonSkill193) {
            return;
        }
        //摊位血量每恢复N2，提升类型N3好汉的初始攻击千分比N4
        int hpCheck = skill193Info.getParamList().get(1);
        if (0 == hpCheck) {
            return;
        }
        int checkBlockType = skill193Info.getParamList().get(2);
        int oldValue = kowloonSkill193.getValue();
        kowloonSkill193.setValue(oldValue + valueAdd);
        int newValue = kowloonSkill193.getValue();

        KowloonBlockEffect effect = new KowloonBlockEffect(eKowloonBlockEffectType.HeroBuff.getValue());
        showResultList.get(showResultList.size() - 1).getSkillEffectList().add(effect);

        if (oldValue / hpCheck != newValue / hpCheck) {
            for (List<KowloonBlock> blockList : listList) {
                for (KowloonBlock block : blockList) {
                    if (null == block) {
                        continue;
                    }
                    if (block.getType() == checkBlockType) {
                        effect.getBlockList().add(block.clone());
                    }
                }
            }
        }
    }

}
